/*
  Time-stamp: <2009-11-14 15:50:11 xinhaoyuan>

  File: iec.c

  This file provide some routines for inter-environment communication
  
 */

#define _EKOS_KERNEL_C_
#include <kernel/kernel.h>

KERNEL_EXPORT void
iec_init(uint16_t n)
{
	 sl_init(&envs[n].iec.send.lock);
	 sl_init(&envs[n].iec.recv.lock);
	 
	 envs[n].iec.send.to = n;
	 envs[n].iec.recv.begin = envs[n].iec.recv.end = n;
}

KERNEL_EXPORT void
iec_process(uint32_t n)
{
	 switch (envs[n].sch_status)
	 {
	 case ENV_SCH_IEC_RECV_BLOCKING:
	 	  /* 这里不使用锁, 需要在顺序上有所注意 */
	 	  if (envs[n].iec.recv.begin != n)
	 	  {
	 		   envs[n].sch_status = ENV_SCH_WAITING;
	 	  }
	 	  break;
	 case ENV_SCH_IEC_SEND_BLOCKING:
	 	  if (envs[n].iec.send.to == n)
	 	  {
	 		   envs[n].sch_status = ENV_SCH_WAITING;
	 	  }
	 }
}

KERNEL_EXPORT int
iec_send(uint16_t n)
{
	 struct env_t *env = envs + cur_env;

	 if (!sl_trylock(&env->iec.send.lock))
		  return -E_IEC_BUSY;
	 
	 if (env->iec.send.to != cur_env)
	 {
		  sl_release(&env->iec.send.lock);
		  return -E_IEC_BUSY;
	 }
	 
	 sl_lock(&envs[n].iec.recv.lock);

	 if (envs[n].iec.recv.policy == IEC_POLICY_DENY)
	 {
		  sl_release(&env->iec.send.lock);
		  sl_release(&envs[n].iec.recv.lock);
		  return -E_IEC_DENIED;
	 }
	 
	 env->iec.send.to = n;
	 env->iec.send.next = n;
	 env->iec.send.prev = envs[n].iec.recv.end;

	 if (envs[n].iec.recv.begin == n)
	 {
		  envs[n].iec.recv.begin = cur_env;
		  envs[n].iec.recv.end = cur_env;
	 }
	 else
	 {
		  uint16_t end = envs[n].iec.recv.end;
		  envs[end].iec.send.next = cur_env;
		  envs[n].iec.recv.end = cur_env;
	 }

	 sl_release(&env->iec.send.lock);
	 sl_release(&envs[n].iec.recv.lock);
	 
	 return 0;
}

KERNEL_EXPORT int
iec_send_cancel(void)
{
	 struct env_t *env = envs + cur_env;

	 if (!sl_trylock(&env->iec.send.lock))
		  return -E_IEC_BUSY;
	 
	 if (env->iec.send.to == cur_env)
	 {
		  sl_release(&env->iec.send.lock);
		  return 0;
	 }

	 int n = env->iec.send.to;	 
	 sl_lock(&envs[n].iec.recv.lock);

	 if (envs[cur_env].iec.send.prev == n)
	 {
		  envs[n].iec.recv.begin = envs[cur_env].iec.send.next;
		  if (envs[n].iec.recv.begin != n)
			   envs[envs[n].iec.recv.begin].iec.send.prev = n;
	 }
	 else envs[envs[cur_env].iec.send.prev].iec.send.next =
			   envs[cur_env].iec.send.next;

	 if (envs[cur_env].iec.send.next == n)
	 {
		  envs[n].iec.recv.end = envs[cur_env].iec.send.prev;
		  if (envs[n].iec.recv.end != n)
			   envs[envs[n].iec.recv.end].iec.send.next = n;
	 }
	 else envs[envs[cur_env].iec.send.next].iec.send.prev =
			   envs[cur_env].iec.send.prev;

	 envs[cur_env].iec.send.to = cur_env;

	 sl_release(&env->iec.send.lock);
	 sl_release(&envs[n].iec.recv.lock);
	 
	 return 0;
}

KERNEL_EXPORT int
iec_recv(uint16_t n)
{
	 struct env_t *env = envs + cur_env;
	 
	 sl_lock(&envs[n].iec.send.lock);

	 if (envs[n].iec.send.to != cur_env)
	 {
		  sl_release(&envs[n].iec.send.lock);
		  return 0;
	 }
	 
	 sl_lock(&env->iec.recv.lock);
	 
	 if (envs[n].iec.send.prev == cur_env)
	 {
		  env->iec.recv.begin = envs[n].iec.send.next;
		  if (env->iec.recv.begin != cur_env)
			   envs[env->iec.recv.begin].iec.send.prev = cur_env;
	 }
	 else envs[envs[n].iec.send.prev].iec.send.next =
			   envs[n].iec.send.next;

	 if (envs[n].iec.send.next == cur_env)
	 {
		  env->iec.recv.end = envs[n].iec.send.prev;
		  if (env->iec.recv.end != cur_env)
			   envs[env->iec.recv.end].iec.send.next = cur_env;
	 }
	 else envs[envs[n].iec.send.next].iec.send.prev =
			   envs[n].iec.send.prev;

	 envs[n].iec.send.to = n;

	 sl_release(&env->iec.recv.lock);
	 sl_release(&envs[n].iec.send.lock);

	 return 0;
}
